package drds.server.buffer;

import java.nio.ByteBuffer;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 竞技场
 * 
 * @author 曹正辉<330937205@qq.com>
 * 
 */
public class Arena implements ByteBufferPool {
	private static final Logger LOGGER = LoggerFactory.getLogger(BlockList.class);
	private final BlockList blockLists[];

	private final AtomicInteger blockNum = new AtomicInteger(0);
	private final AtomicInteger failCount = new AtomicInteger(0);

	private static final int FAIL_THRESHOLD = 1000;
	private final int pageSize;
	private final int blockSize;

	private final AtomicLong capacity;
	private final AtomicLong size;

	private final ConcurrentHashMap<Thread, Integer> threadAndCountMap;

	private final int readByteBufferBlock;

	public Arena(//
			int blockSize,//
			int pageSize, //
			int blockNum,//
			int readByteBufferBlock) {//
		try {
			this.blockSize = blockSize;
			this.pageSize = pageSize;
			this.blockNum.set(blockNum);
			this.readByteBufferBlock = readByteBufferBlock;

			blockLists = new BlockList[6];
			blockLists[5] = new BlockList(blockSize, pageSize, 100, Integer.MAX_VALUE, 0);
			blockLists[4] = new BlockList(blockSize, pageSize, 75, 100, 0);
			blockLists[3] = new BlockList(blockSize, pageSize, 50, 100, 0);
			blockLists[2] = new BlockList(blockSize, pageSize, 25, 75, 0);
			blockLists[1] = new BlockList(blockSize, pageSize, 1, 50, 0);
			blockLists[0] = new BlockList(blockSize, pageSize, Integer.MIN_VALUE, 25, blockNum);

			blockLists[0].next = blockLists[1];
			blockLists[1].next = blockLists[2];
			blockLists[2].next = blockLists[3];
			blockLists[3].next = blockLists[4];
			blockLists[4].next = blockLists[5];
			blockLists[5].next = null;

			blockLists[5].prev = blockLists[4];
			blockLists[4].prev = blockLists[3];
			blockLists[3].prev = blockLists[2];
			blockLists[2].prev = blockLists[1];
			blockLists[1].prev = blockLists[0];
			blockLists[0].prev = null;

			capacity = new AtomicLong(6 * blockNum * blockSize);
			size = new AtomicLong(6 * blockNum * blockSize);
			threadAndCountMap = new ConcurrentHashMap<Thread, Integer>();
		} finally {
		}
	}

	@Override
	public ByteBuffer allocate(int size) {
		try {
			ByteBuffer byteBuffer = null;
			int i = 0;
			int count = 0;
			while (byteBuffer == null) {
				if (i > 5) {
					i = 0;
					count = failCount.incrementAndGet();
					if (count > FAIL_THRESHOLD) {
						try {
							expand();
						} finally {
						}
					}
				}
				byteBuffer = blockLists[i].allocateByteBuffer(size);
				i++;
			}
			capacity.addAndGet(-size);
			final Thread thread = Thread.currentThread();
			if (threadAndCountMap.contains(thread)) {
				int currentCount = threadAndCountMap.get(thread);
				currentCount++;
				threadAndCountMap.put(thread, currentCount);
			} else {
				threadAndCountMap.put(thread, 0);
			}
			return byteBuffer;
		} finally {
		}
	}

	private void expand() {
		Block block = new Block(blockSize, pageSize);
		blockLists[0].blockSet.add(block);
		failCount.set(0);
	}

	@Override
	public void recycle(ByteBuffer byteBuffer) {
		try {
			int i;
			for (i = 0; i < 6; i++) {
				if (blockLists[i].free(byteBuffer)) {
					break;
				}
			}
			if (i > 5) {
				LOGGER.error("This ByteBuffer is not maintained in ByteBufferArena!");
				return;
			}
			final Thread thread = Thread.currentThread();
			if (threadAndCountMap.contains(thread)) {
				int currentCount = threadAndCountMap.get(thread);
				currentCount--;
				threadAndCountMap.put(thread, currentCount);
			} else {
				threadAndCountMap.put(thread, 0);
			}
			capacity.addAndGet(byteBuffer.capacity());
			return;
		} finally {
		}
	}

	@Override
	public long capacity() {
		return capacity.get();
	}

	@Override
	public long size() {
		return size.get();
	}

	@Override
	public int getReadByteBufferBlock() {
		return readByteBufferBlock;
	}

	@Override
	public int getAllCount() {
		final Set<Integer> countSet = (Set<Integer>) threadAndCountMap.values();
		int count = 0;
		for (int i : countSet) {
			count += i;
		}
		return count;
	}

	/**
	 * 这里pageSize就是DirectByteBuffer的chunksize
	 */
	@Override
	public int getBlockSize() {
		return pageSize;
	}

	@Override
	public ByteBufferList byteBufferList() {
		return new ByteBufferList(this);
	}
}
